package com.hxzy.tms.service.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.web.util.HtmlUtils;

import com.hxzy.tms.Assert;
import com.hxzy.tms.SessionHolder;
import com.hxzy.tms.dao.BaseDao.Params;
import com.hxzy.tms.dao.ProjectDao;
import com.hxzy.tms.dao.TaskDao;
import com.hxzy.tms.dao.TeamDao;
import com.hxzy.tms.dao.TeamUserDao;
import com.hxzy.tms.dao.UserTaskDao;
import com.hxzy.tms.entity.Project;
import com.hxzy.tms.entity.Task;
import com.hxzy.tms.entity.Team;
import com.hxzy.tms.entity.User;
import com.hxzy.tms.entity.UserTask;
import com.hxzy.tms.service.ServiceResult;
import com.hxzy.tms.service.TaskService;

public class DefaultTaskService implements TaskService {
    private Logger logger = LoggerFactory.getLogger(getClass());

    private TaskDao taskDao;
    private TeamDao teamDao;
    private TeamUserDao teamUserDao;
    private ProjectDao projectDao;
    private UserTaskDao userTaskDao;

    public DefaultTaskService(TaskDao taskDao, TeamDao teamDao, ProjectDao projectDao, TeamUserDao teamUserDao,
            UserTaskDao userTaskDao) {
        super();
        this.taskDao = taskDao;
        this.teamDao = teamDao;
        this.projectDao = projectDao;
        this.teamUserDao = teamUserDao;
        this.userTaskDao = userTaskDao;
    }

    public ServiceResult<Task> read(long taskId) {
        Task task = taskDao.read(taskId);
        if (task == null) {
            return new ServiceResult<Task>(1, null);
        }
        logger.debug("team id {}", task.getTeam().getId());
        logger.debug("project id {}", task.getProject().getId());
        return new ServiceResult<Task>(1, task);
    }

    public ServiceResult<Long> count(long teamId, Long projectId) {
        String hql = " select count(*) from Task where team.id = :teamId";

        List<Params> params = new ArrayList<Params>();
        params.add(new Params("teamId", teamId));
        if (projectId != null) {
            hql += " and project.id = :projectId";
            params.add(new Params("projectId", projectId));
        }
        List<?> list = taskDao.read(hql, params);
        return new ServiceResult<Long>(1, new Long(list.get(0).toString()));
    }

    public ServiceResult<Map<Integer, List<Task>>> list(long teamId, Long projectId) {
        String hql = " from Task where team.id = :teamId"; // FIXME:
                                                           // 不想查出来的数据，hibernate
                                                           // 就是麻烦
        List<Params> params = new ArrayList<Params>();
        params.add(new Params("teamId", teamId));
        if (projectId != null) {
            hql += " and project.id = :projectId";
            params.add(new Params("projectId", projectId));
        }
        hql += " ORDER BY gmtStartYear DESC, gmtStartMonth DESC, gmtStartDate DESC ";
        List<Task> tasks2 = taskDao.read(hql, params), tasks = new LinkedList<Task>();
        for (Task t : tasks2) {
            Task _t = new Task();
            BeanUtils.copyProperties(t, _t, "team", "project");
            _t.setTitle(HtmlUtils.htmlEscape(t.getTitle()));
            _t.setDesc(HtmlUtils.htmlEscape(t.getDesc()));
            _t.setAttachmentUrl(HtmlUtils.htmlEscape(t.getAttachmentUrl()));
            tasks.add(_t);
        }

        Map<Integer, List<Task>> tasksMap = new TreeMap<Integer, List<Task>>(Collections.reverseOrder());
        if (tasks.isEmpty()) {
            return new ServiceResult<Map<Integer, List<Task>>>(1, tasksMap);
        }

        if (tasks.size() == 1) {
            Task _t = tasks.get(0);
            tasksMap.put(taskKey(_t.getGmtStartYear(), _t.getGmtStartMonth(), _t.getGmtStartDate()), tasks);
            return new ServiceResult<Map<Integer, List<Task>>>(1, tasksMap);
        }

        // 把排序好的数据组织成 key -> value 形式，按 key 倒排
        List<Task> _tasks = new LinkedList<Task>();
        Task _t = tasks.get(0);
        _tasks.add(_t);
        for (int i = 1, j = tasks.size(); i < j; i++) {
            Task t = tasks.get(i);
            Integer _key = taskKey(_t.getGmtStartYear(), _t.getGmtStartMonth(), _t.getGmtStartDate());
            Integer key = taskKey(t.getGmtStartYear(), t.getGmtStartMonth(), t.getGmtStartDate());
            if (!_key.equals(key)) {
                _t = t;
                tasksMap.put(_key, _tasks);
                _tasks = new LinkedList<Task>();
            }
            _tasks.add(t);
            if (i == (j - 1)) {
                tasksMap.put(key, _tasks); // 最后一条记录要把当前数据加入进去
            }
        }

        return new ServiceResult<Map<Integer, List<Task>>>(1, tasksMap);
    }

    public ServiceResult<List<Task>> list(Long projectId, int offset, int limit) {
        Project p = projectDao.read(projectId);
        if (p == null) {
            return new ServiceResult<List<Task>>(1, Collections.<Task>emptyList());
        }
        return this.list(p.getTeam().getId(), projectId, offset, limit);
    }

    public ServiceResult<List<Task>> list(long teamId, Long projectId, int offset, int limit) {
        String hql = " from Task where team.id = :teamId";

        List<Params> params = new ArrayList<Params>();
        params.add(new Params("teamId", teamId));
        if (projectId != null) {
            hql += " and project.id = :projectId";
            params.add(new Params("projectId", projectId));
        }
        hql += " ORDER BY gmtCreate DESC ";

        List<Task> tasks2 = taskDao.read(hql, params, offset, limit), tasks = new LinkedList<Task>();
        for (Task t : tasks2) {
            Task _t = new Task();
            BeanUtils.copyProperties(t, _t, "team", "project");
            _t.setTitle(HtmlUtils.htmlEscape(t.getTitle()));
            _t.setDesc(HtmlUtils.htmlEscape(t.getDesc()));
            _t.setAttachmentUrl(HtmlUtils.htmlEscape(t.getAttachmentUrl()));
            tasks.add(_t);
        }

        return new ServiceResult<List<Task>>(1, tasks);
    }

    public ServiceResult<List<UserTask>> list(long userId) {
        Assert.greaterThan(userId, 0L, "user id must > 0");
        String hql = " from UserTask where user.id = :userId order by gmtCreate desc";
        List<UserTask> userTasks = userTaskDao.read(hql, new Params("userId", userId));
        return new ServiceResult<List<UserTask>>(1, userTasks);
    }

    public ServiceResult<Task> create(long teamId, long projectId, String title, String desc, String attachmentUrl,
            Date gmtStart, Date gmtFinished) {
        Assert.notEmpty(SessionHolder.get(), "must be login.");

        Assert.greaterThan(teamId, 0L, "team id must > 0");
        Assert.greaterThan(projectId, 0L, "team id must > 0");
        Assert.notEmpty(gmtStart, "gmtStart must not be null or empty");
        Assert.notEmpty(gmtStart, "gmtFinished must not be null or empty");
        Assert.greaterThan(gmtFinished.getTime(), gmtStart.getTime(), "gmtFinished must > gmtStart");
        Assert.notBlank(title, "title must not be null or blank.");
        Assert.notBlank(desc, "desc must not be null or blank.");
        // Assert.notBlank(attachmentUrl, "attachmentUrl must not be null or
        // blank.");

        List<User> users = teamUserDao.teamUsers(teamId);
        if (users == null || users.isEmpty()) {
            return new ServiceResult<Task>(-1, null, "团队下还没有成员，请添加成员之后再发布任务");
        }

        // 1. 写入 task 表
        Task task = new Task();
        int now = (int) (System.currentTimeMillis() / 1000);
        Team team = teamDao.read(Team.class, teamId);
        Assert.notEmpty(gmtStart, "team must not be null");
        if (!team.getUserCreate().getId().equals(SessionHolder.get().getId())) {
            return new ServiceResult<Task>(-2, null, "登录用户不是团队的创建者，只有创建者才能发布任务");
        }
        Project project = projectDao.read(Project.class, projectId);
        Assert.notEmpty(gmtStart, "projectId must not be null");
        if (!project.getTeam().getId().equals(team.getId())) {
            return new ServiceResult<Task>(-3, null, "项目不是属于该团队，只有创建者才能发布任务");
        }

        Calendar cal = Calendar.getInstance();
        cal.setTime(gmtStart);

        task.setTitle(title);
        task.setDesc(desc);
        task.setAttachmentUrl(StringUtils.isBlank(attachmentUrl) ? StringUtils.EMPTY : attachmentUrl);
        task.setGmtStart((int) (gmtStart.getTime() / 1000));
        task.setGmtStartYear(cal.get(Calendar.YEAR));
        task.setGmtStartMonth(cal.get(Calendar.MONTH) + 1);
        task.setGmtStartDate(cal.get(Calendar.DATE));

        cal.setTime(gmtFinished);
        task.setGmtFinished((int) (gmtFinished.getTime() / 1000));
        task.setGmtFinishedYear(cal.get(Calendar.YEAR));
        task.setGmtFinishedMonth(cal.get(Calendar.MONTH) + 1);
        task.setGmtFinishedDate(cal.get(Calendar.DATE));

        task.setUserCreate(SessionHolder.get().getId());
        task.setTeam(team);
        task.setProject(project);
        task.setGmtCreate(now);
        task.setGmtModified(0);
        taskDao.create(task);

        // 2. 写入 user_task 表
        for (User user : users) {
            UserTask ut = new UserTask();
            ut.setRemark("");
            ut.setAttachmentUrl("");
            ut.setGmtCreate(now);
            ut.setGmtExpires((int) (gmtFinished.getTime() / 1000));

            ut.setTask(task);
            ut.setTeam(team);
            ut.setProject(project);
            ut.setGmtModified(0);
            ut.setStatus(UserTask.STATUS_NORMAL);

            User _u = new User();
            _u.setId(user.getId());
            ut.setUser(_u);

            ut.setUidCreate(SessionHolder.get().getId());
            ut.setUidModified(0L);
            this.userTaskDao.create(ut);
        }

        return new ServiceResult<Task>(1, task, String.format("任务成功发布给 %d 个成员", users.size()));
    }

    public ServiceResult<Task> update(long taskId, String title, String desc, String attachmentUrl, Date gmtStart,
            Date gmtFinished) {
        Assert.notEmpty(SessionHolder.get(), "must be login.");

        Assert.greaterThan(taskId, 0L, "task id must > 0");
        Assert.notEmpty(gmtStart, "gmtStart must not be null or empty");
        Assert.notEmpty(gmtStart, "gmtFinished must not be null or empty");
        Assert.greaterThan(gmtFinished.getTime(), gmtStart.getTime(), "gmtFinished must > gmtStart");
        Assert.notBlank(title, "title must not be null or blank.");
        Assert.notBlank(desc, "desc must not be null or blank.");
        Assert.notBlank(attachmentUrl, "attachmentUrl must not be null or blank.");

        int now = (int) (System.currentTimeMillis() / 1000);

        Task task = taskDao.read(Task.class, taskId);
        String oldTitle = task.getTitle();
        Calendar cal = Calendar.getInstance();

        task.setTitle(title);
        task.setDesc(desc);
        task.setAttachmentUrl(attachmentUrl);

        cal.setTime(gmtStart);
        task.setGmtStart((int) (gmtStart.getTime() / 1000));
        task.setGmtStartYear(cal.get(Calendar.YEAR));
        task.setGmtStartMonth(cal.get(Calendar.MONTH) + 1);
        task.setGmtStartDate(cal.get(Calendar.DATE));

        cal.setTime(gmtFinished);
        task.setGmtFinished((int) (gmtFinished.getTime() / 1000));
        task.setGmtFinishedYear(cal.get(Calendar.YEAR));
        task.setGmtFinishedMonth(cal.get(Calendar.MONTH) + 1);
        task.setGmtFinishedDate(cal.get(Calendar.DATE));

        task.setUserModified(SessionHolder.get().getId());
        task.setGmtModified(now);
        int result = taskDao.update(task);
        if (result == 1) {
            return new ServiceResult<Task>(1, task, String.format("任务修改成功 {} -> {} ", oldTitle, title));
        } else {
            return new ServiceResult<Task>(-1);
        }
    }

    public ServiceResult<List<UserTask>> userTasks(long taskId) {
        List<UserTask> userTasks = userTaskDao.read(" from UserTask where task.id = :taskId order by gmtCreate desc",
                new Params("taskId", taskId));
        return new ServiceResult<List<UserTask>>(1, userTasks);
    }

    public ServiceResult<UserTask> commit(long taskId, String attachmentUrl, String remark) {
        Assert.notEmpty(SessionHolder.get(), "must be login.");

        Assert.greaterThan(taskId, 0L, "task id must > 0");

        List<UserTask> userTasks = userTaskDao.read(" from UserTask where task.id = :taskId and user.id = :userId",
                new Params("taskId", taskId), new Params("userId", SessionHolder.get().getId()));
        if (userTasks.isEmpty() || userTasks.size() > 1) {
            return new ServiceResult<UserTask>(-1, null, "任务不存在");
        }

        UserTask userTask = userTasks.get(0);
        userTask.setRemark(remark);
        userTask.setAttachmentUrl(attachmentUrl);
        userTask.setStatus(UserTask.STATUS_COMMITED);
        userTask.setGmtModified((int) (System.currentTimeMillis() / 1000));
        userTask.setUidModified(SessionHolder.get().getId());
        UserTask t = userTaskDao.merge(userTask);

        return new ServiceResult<UserTask>(1, t);
    }

    public ServiceResult<UserTask> finish(long userTaskId) {
        Assert.notEmpty(SessionHolder.get(), "must be login.");

        Assert.greaterThan(userTaskId, 0L, "user task id must > 0");

        UserTask userTask = userTaskDao.read(UserTask.class, userTaskId);
        if (userTask == null) {
            return new ServiceResult<UserTask>(-1, null, "任务不存在");
        }
        if (!userTask.getStatus().equals(UserTask.STATUS_COMMITED)) {
            return new ServiceResult<UserTask>(-2, null, "只有已提交的任务才能被结束任务");
        }
        if (!userTask.getUidCreate().equals(SessionHolder.get().getId())) {
            return new ServiceResult<UserTask>(-3, null, "只有任务发布人才能关闭任务");
        }
        userTask.setStatus(UserTask.STATUS_FINISHED);
        userTask.setGmtModified((int) (System.currentTimeMillis() / 1000));
        userTask.setUidModified(SessionHolder.get().getId());
        UserTask t = userTaskDao.merge(userTask);

        return new ServiceResult<UserTask>(1, t);
    }

    private Integer taskKey(int year, int month, int date) {
        return Integer.parseInt(String.format("%d%d%d", year, month, date));
    }

}
